Skip to content

fix: Robust Recursion Handling & Enhanced Migration Tools#109

Open
zerodiscount wants to merge 1 commit intoagritheory:version-15from
zerodiscount:pr-contribution
Open

fix: Robust Recursion Handling & Enhanced Migration Tools#109
zerodiscount wants to merge 1 commit intoagritheory:version-15from
zerodiscount:pr-contribution

Conversation

@zerodiscount
Copy link
Copy Markdown

This PR introduces several stability improvements and tools for the Cloud Storage app, developed during a large-scale enterprise migration.

Key Changes:

  1. Fix Recursion Error:

    • Addressed a \RecursionError\ in \CloudStorageFile.validate. The recursive loop occurred when \�ssociate_files()\ called \save(), re-triggering validation. This fix checks \ignore_file_validate\ before calling association logic.
  2. Robust Migration Tools:

    • Updated \migrate-files-to-cloud-storage:
      • Smart Sync: Checks if file already exists in S3 (\head_object) before uploading. If found, it syncs the DB record (\s3_key) and skips upload. This allows re-running migrations safely.
      • Validation Bypass: Added \ignore_links, \ignore_permissions, and \ignore_validate\ flags to \ ile_doc. This allows migrating historical files even if their parent documents (\�ttached_to_name) are missing or corrupted (common in legacy databases), preventing \LinkValidationError.
  3. File Logic Hardening:

    • Updated \write_file\ and \�ssociate_files\ in \overrides/file.py\ to correctly propagate the above validation flags (\ignore_links, etc.) when updating existing file records.
    • Patched \has_permission\ to gracefully handle \DoesNotExistError\ when checking permissions on files attached to deleted documents.

These changes make the app significantly more robust for real-world production environments and bulk migrations.

This commit introduces several stability improvements and tools for the Cloud Storage app, developed during a large-scale enterprise migration.

1.  **Fix Recursion Error**:
    *   Addressed a RecursionError in CloudStorageFile.validate. The recursive loop occurred when associate_files() called save(), re-triggering validation. This fix checks ignore_file_validate *before* calling association logic.

2.  **Robust Migration Tools**:
    *   Updated migrate-files-to-cloud-storage:
        *   **Smart Sync**: Checks if file already exists in S3 (head_object) before uploading. If found, it syncs the DB record (s3_key) and skips upload.
        *   **Validation Bypass**: Added ignore_links, ignore_permissions, and ignore_validate flags to file_doc.

3.  **File Logic Hardening**:
    *   Updated write_file and associate_files in overrides/file.py to correctly propagate validation flags when updating *existing* file records.
    *   Patched has_permission to gracefully handle DoesNotExistError.
@agritheory agritheory requested a review from lauty95 January 14, 2026 15:30
Comment on lines +455 to +473
def get_cloud_storage_config() -> dict:
config = frappe.conf.get("cloud_storage_settings", {})

# If nested config is found and seems populated with at least access_key, use it.
if config and config.get("access_key"):
return config

# Otherwise, build from top-level standard keys
return {
"access_key": frappe.conf.get("s3_access_key"),
"secret": frappe.conf.get("s3_secret_key"),
"bucket": frappe.conf.get("s3_bucket"),
"region": frappe.conf.get("region"),
"endpoint_url": frappe.conf.get("endpoint_url"),
"folder": frappe.conf.get("s3_folder"),
"use_local": frappe.conf.get("use_local"),
"use_legacy_paths": frappe.conf.get("use_legacy_paths", True)
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although this function is a good idea, I recommend using the same keys documented in the configuration guide: https://github.com/agritheory/cloud_storage/blob/version-15/docs/configuration.md

This would maintain consistency with the official documentation and avoid confusion for users following the setup instructions.

Comment on lines +609 to +610
if hasattr(frappe.local, "site") and frappe.local.site:
return f"{frappe.local.site}/{path}"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is not necessary. If you need a custom path, you can configure it in site_config.json:

{
  "cloud_storage_settings": {
    "folder": "site1.local/folder",
    ...
  }
}

This approach is more flexible and doesn't force a specific path structure on all installations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants